package com.lijiajia.cloud.authserver.configuration.security.authserver;

import com.lijiajia.cloud.authserver.security.oauth.client.MongoClientDetailsService;
import com.lijiajia.cloud.authserver.service.ClientService;
import com.lijiajia.cloud.common.security.oauth2.token.provider.CloudUserAuthenticationConverter;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.security.oauth2.authserver.AuthorizationServerProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.Resource;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.token.DefaultAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.UserAuthenticationConverter;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
import org.springframework.security.oauth2.provider.token.store.KeyStoreKeyFactory;
import org.springframework.util.Assert;

import java.util.Optional;

/**
 * 授权服务器 配置
 *
 * @author lijiajia
 * @since 2019-05-28
 */
@EnableAuthorizationServer
@EnableConfigurationProperties(AuthorizationServerProperties.class)
@Configuration
public class AuthorizationServerConfiguration implements ApplicationContextAware {
	private ApplicationContext context;
	private final AuthorizationServerProperties authorization;


	public AuthorizationServerConfiguration(
		ApplicationContext context,
		AuthorizationServerProperties authorization) {
		this.context = context;
		this.authorization = authorization;
	}

	@Override
	public void setApplicationContext(ApplicationContext context) throws BeansException {
		this.context = context;
	}

	/**
	 * 客户端秘钥 加密方式
	 */
	@Bean
	@Qualifier("clientPasswordEncoder")
	public PasswordEncoder clientSecretPasswordEncoder() {
		return new BCryptPasswordEncoder();
	}

	@Bean
	@Qualifier("authServerTokenEnhancer")
	public TokenEnhancer tokenEnhancer() {
		return accessTokenConverter();
	}

	@Bean
	@Qualifier("authServerAccessTokenConverter")
	public JwtAccessTokenConverter accessTokenConverter() {
		Assert.notNull(this.authorization.getJwt().getKeyStore(), "keyStore cannot be null");
		Assert.notNull(this.authorization.getJwt().getKeyStorePassword(), "keyStorePassword cannot be null");
		Assert.notNull(this.authorization.getJwt().getKeyAlias(), "keyAlias cannot be null");

		JwtAccessTokenConverter converter = new JwtAccessTokenConverter();

		Resource keyStore = this.context.getResource(this.authorization.getJwt().getKeyStore());
		char[] keyStorePassword = this.authorization.getJwt().getKeyStorePassword().toCharArray();
		KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(keyStore, keyStorePassword);

		String keyAlias = this.authorization.getJwt().getKeyAlias();
		char[] keyPassword = Optional.ofNullable(this.authorization.getJwt().getKeyPassword()).map(String::toCharArray).orElse(keyStorePassword);
		converter.setKeyPair(keyStoreKeyFactory.getKeyPair(keyAlias, keyPassword));

		UserAuthenticationConverter userAuthenticationConverter = new CloudUserAuthenticationConverter();
		DefaultAccessTokenConverter accessTokenConverter = new DefaultAccessTokenConverter();
		accessTokenConverter.setUserTokenConverter(userAuthenticationConverter);

		converter.setAccessTokenConverter(accessTokenConverter);
		return converter;
	}

	/**
	 * 客户端服务 bean配置
	 */
	@Configuration
	public class ClientDetailsServiceConfiguration {
		private final ClientService clientService;

		public ClientDetailsServiceConfiguration(ClientService clientService) {
			this.clientService = clientService;
		}

		/**
		 * oauth2 客户端服务
		 */
		@Bean
		@Qualifier("authServerClientDetailsService")
		@Primary
		public ClientDetailsService mongoClientDetailsService() {
			return new MongoClientDetailsService(clientService);
		}
	}

	/**
	 * token store 配置
	 */
	@Configuration
	public class TokenStoreConfiguration {
		private final JwtAccessTokenConverter accessTokenConverter;

		public TokenStoreConfiguration(JwtAccessTokenConverter accessTokenConverter) {
			this.accessTokenConverter = accessTokenConverter;
		}

		@Bean("authServerTokenStore")
		public TokenStore tokenStore() {
			return new JwtTokenStore(accessTokenConverter);
		}
	}


}
